import Router from 'koa-router';
import type { AppContext } from '../types/index.js';
import { logger } from './logger.js';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs/promises';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export class RouteLoader {
  public router: Router<object, AppContext>;
  private routesPath: string;

  constructor(routesPath: string = '../routes') {
    this.router = new Router<object, AppContext>();
    this.routesPath = path.resolve(__dirname, routesPath);
  }

  // 根据文件路径生成路由前缀
  private generatePrefix(filePath: string): string {
    // 获取相对于 routes 目录的路径
    const relativePath = path.relative(this.routesPath, filePath);
    // 获取文件名（不含扩展名）
    const fileName = path.basename(relativePath).replace(/\.(ts|js)$/, '');
    // 获取目录路径
    const dirPath = path.dirname(relativePath);

    // 如果是 index 文件，返回目录路径作为前缀
    if (fileName === 'index') {
      return dirPath === '.' ? '/' : '/' + dirPath.replace(/\\/g, '/');
    }

    // 非 index 文件，返回目录路径 + 文件名作为前缀
    const prefix =
      dirPath === '.' ? '/' + fileName : '/' + dirPath.replace(/\\/g, '/') + '/' + fileName;

    return prefix;
  }

  // 加载路由文件
  private async loadRouteFile(filePath: string): Promise<void> {
    try {
      // 将文件路径转换为 URL 格式
      const fileUrl = `file://${filePath.replace(/\\/g, '/')}`;
      const route: Record<string, unknown> = await import(fileUrl);
      if (route.default) {
        // 生成路由前缀
        const prefix = this.generatePrefix(filePath);
        logger.info(`加载路由文件: ${filePath} (前缀: ${prefix})`);

        // 创建新的路由实例并设置前缀
        const newRouter = new Router<object, AppContext>();
        // 复制原路由的所有路由
        (
          route.default as {
            stack: Array<{
              path: string;
              methods: string[];
              stack: Array<{ method: string; path: string }>;
            }>;
          }
        ).stack.forEach(layer => {
          // 构建完整的路由路径
          let layerPath = layer.path;
          // 如果路由路径以 / 开头且前缀不是根路径，需要去掉开头的 /
          if (layerPath.startsWith('/') && prefix !== '/') {
            layerPath = layerPath.substring(1);
          }
          const fullPath = prefix === '/' ? layerPath : prefix + '/' + layerPath;
          // 创建新的路由层
          (
            newRouter.stack as unknown as Array<{
              path: string;
              methods: string[];
              stack: Array<{ method: string; path: string }>;
              regexp?: RegExp;
            }>
          ).push({
            ...layer,
            path: fullPath,
            regexp: new RegExp('^' + fullPath.replace(/:[^/]+/g, '([^/]+)') + '$'),
          });
        });
        this.router.use(newRouter.routes());
      }
    } catch (error) {
      logger.error(`路由加载失败: ${filePath}`, { error });
    }
  }

  // 递归加载目录
  private async loadDirectory(dirPath: string): Promise<void> {
    try {
      const files = await fs.readdir(dirPath);

      for (const file of files) {
        const filePath = path.join(dirPath, file);
        const stat = await fs.stat(filePath);

        if (stat.isDirectory()) {
          await this.loadDirectory(filePath);
        } else if ((file.endsWith('.ts') || file.endsWith('.js')) && !file.endsWith('.d.ts')) {
          await this.loadRouteFile(filePath);
        }
      }
    } catch (error) {
      logger.error(`目录加载失败: ${dirPath}`, { error });
    }
  }

  // 加载所有路由
  async load(): Promise<Router<object, AppContext>> {
    try {
      await this.loadDirectory(this.routesPath);
      return this.router;
    } catch (error) {
      logger.error('路由加载失败', { error });
      throw error;
    }
  }
}

// TODO 解决跟路由路径问题

export async function initRouter(): Promise<Router<object, AppContext>> {
  try {
    // 创建路由加载器实例
    const routeLoader = new RouteLoader();
    const router = await routeLoader.load();
    logger.info('路由加载完成');
    return router;
  } catch (error) {
    logger.error('路由加载失败', { error });
    throw error;
  }
}
